#include <iostream>
#include <thread>
#include <chrono>
#include <random>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>

using namespace std;

mutex mtx;
condition_variable cv;
bool ack_received = false;
bool packet_lost = false;

const int WINDOW_SIZE = 4;
const int MAX_RETRIES = 5;
const int PACKET_LOSS_PROBABILITY = 30;

random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dis(1, 100);

bool is_packet_lost() {
    return dis(gen) <= PACKET_LOSS_PROBABILITY;
}

struct Packet {
    int id;
    int retries = 0;
};

enum WindowState {
    SENT_ACKED,
    SENT_UNACKED,
    ALLOWED_NOT_SENT,
    NOT_ALLOWED_SENT,
    DELIVERED,
    ALLOWED_RECEIVE,
    NOT_ALLOWED_RECEIVE
};

queue<Packet> sender_window;
queue<Packet> receiver_window;

void sender() {
    int packet_id = 0;

    while (true) {
        this_thread::sleep_for(chrono::seconds(1));

        if (sender_window.size() < WINDOW_SIZE) {
            Packet pkt{ packet_id };
            sender_window.push(pkt);
            cout << "Sender: Sending packet " << packet_id << endl;
        }

        unique_lock<mutex> lock(mtx);
        ack_received = false;
        packet_lost = is_packet_lost();

        if (!packet_lost && !sender_window.empty()) {
            Packet pkt = sender_window.front();
            cout << "Sender: Packet " << pkt.id << " sent (waiting for ACK)" << endl;
            cv.notify_all();
        }
        else {
            cout << "Sender: Packet lost, retrying..." << endl;
        }

        if (cv.wait_for(lock, chrono::seconds(1), [] { return ack_received; })) {
            cout << "Sender: ACK received for packet " << sender_window.front().id << endl;
            sender_window.pop();
        }
        else {
            cout << "Sender: Timeout for packet " << sender_window.front().id << ", resending..." << endl;
            if (sender_window.front().retries < MAX_RETRIES) {
                sender_window.front().retries++;
            }
            else {
                cout << "Sender: Max retries reached for packet " << sender_window.front().id << endl;
                sender_window.pop();
            }
        }
    }
}

void receiver() {
    int packet_id = 0;
    while (true) {
        this_thread::sleep_for(chrono::seconds(1));

        unique_lock<mutex> lock(mtx);
        cv.wait(lock, [] { return !sender_window.empty(); });

        if (!packet_lost && !sender_window.empty()) {
            Packet pkt = sender_window.front();
            cout << "Receiver: Received packet " << pkt.id << endl;
            ack_received = true;
            cout << "Receiver: Sending ACK for packet " << pkt.id << endl;
        }

        cv.notify_all();
    }
}

void print_window_status() {
    this_thread::sleep_for(chrono::seconds(1));

    cout << "Window status:" << endl;

    cout << "  Sent and acknowledged: ";
    for (const auto& pkt : sender_window) {
        if (pkt.retries == 0) {
            cout << pkt.id << " ";
        }
    }

    cout << "\n  Sent but unacknowledged: ";
    for (const auto& pkt : sender_window) {
        if (pkt.retries > 0) {
            cout << pkt.id << " ";
        }
    }

    cout << "\n  Allowed but not sent: ";
    cout << "None";

    cout << "\n  Not allowed to send: ";
    cout << "None";

    cout << "\n  Delivered to host: ";
    cout << "None";

    cout << "\n  Allowed to receive: ";
    cout << "None";

    cout << "\n  Not allowed to receive: ";
    cout << "None";
}

int main() {
    thread sender_thread(sender);
    thread receiver_thread(receiver);
    thread status_thread(print_window_status);

    sender_thread.join();
    receiver_thread.join();
    status_thread.join();

    return 0;
}
